package org.dolphin.dao.impl;

import java.io.Serializable;
import java.util.List;
import org.dolphin.base.bean.FlatProductAttributeVO;
import org.dolphin.base.bean.FlatProductItemSearchVO;
import org.dolphin.base.bean.FlatProductItemVO;
import org.dolphin.dao.ProductAttributeDao;
import org.dolphin.dao.util.SessionHelper;
import org.dolphin.entity.inventory.ProductAttribute;
import org.dolphin.util.Nvl;
import org.hibernate.Criteria;
import org.hibernate.FetchMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.sql.JoinType;
import org.hibernate.transform.AliasToBeanResultTransformer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository(value = "productAttributeDao")
public class ProductAttributeDaoImpl implements ProductAttributeDao {
  @Autowired(required = true)
  private SessionFactory sessionFactory;

  protected DetachedCriteria buildProductAttributeCriteria() {
    DetachedCriteria dc = DetachedCriteria.forClass(ProductAttribute.class);
    return dc;
  }

  @Override
  public ProductAttribute fetchProductAttribute(Serializable id) {
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    ProductAttribute attribute = (ProductAttribute) session.get(ProductAttribute.class, id);
    return attribute;
  }

  @SuppressWarnings("unchecked")
  @Override
  public ProductAttribute fetchProductAttribute(Serializable id, boolean fetchProduct) {
    if (!fetchProduct) {
      return fetchProductAttribute(id);
    }
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    DetachedCriteria dc = buildProductAttributeCriteria();
    dc.add(Restrictions.idEq(id));
    dc.setFetchMode("product", FetchMode.JOIN);
    Criteria criteria = dc.getExecutableCriteria(session);
    List<ProductAttribute> list = criteria.list();
    if (list != null && !list.isEmpty()) {
      return list.get(0);
    }
    return null;
  }

  @Override
  public ProductAttribute getProductAttribute(String sku) {
    ProductAttribute productAttribute = getProductAttribute(sku, null);
    return productAttribute;
  }

  @SuppressWarnings("unchecked")
  @Override
  public ProductAttribute getProductAttribute(String sku, Serializable excludedId) {
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    DetachedCriteria dc = buildProductAttributeCriteria();
    dc.add(Restrictions.eq("stockKeepingUnit", sku));
    if (excludedId != null) {
      dc.add(Restrictions.not(Restrictions.idEq(excludedId)));
    }
    Criteria criteria = dc.getExecutableCriteria(session);
    List<ProductAttribute> list = criteria.list();
    if (list != null && !list.isEmpty()) {
      return list.get(0);
    }
    return null;
  }

  @SuppressWarnings("unchecked")
  @Override
  public List<FlatProductAttributeVO> getProductAttributes(Serializable productId, int firstResult,
      int maxResults, List<Order> orders) {
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    StringBuilder builder = new StringBuilder();
    builder.append("SELECT pa.id AS id, pa.name AS name, pa.stockKeepingUnit AS sku, ");
    builder.append("pa.version AS version, p.baseRetailPrice + pa.adjustmentRetailPrice ");
    builder.append("AS finalRetailPrice FROM ");
    builder.append(ProductAttribute.class.getName());
    builder.append(" pa JOIN pa.product p ");
    if (productId != null) {
      builder.append(" WHERE p.id = :productId ");
    }
    if (orders != null && !orders.isEmpty()) {
      builder.append(" ORDER BY ");
      for (int i = 0; i < orders.size(); i++) {
        Order order = orders.get(i);
        String sql = order.toString();
        builder.append(sql);
        if (i < orders.size() - 1) {
          builder.append(", ");
        }
      }
    }
    String hql = builder.toString();
    Query query = session.createQuery(hql);
    if (productId != null) {
      query.setString("productId", (String) productId);
    }
    query.setResultTransformer(new AliasToBeanResultTransformer(FlatProductAttributeVO.class));
    if (firstResult > 0) {
      query.setFirstResult(firstResult);
    }
    if (maxResults > 0) {
      query.setMaxResults(maxResults);
    }
    List<FlatProductAttributeVO> list = query.list();
    return list;
  }

  @Override
  public Long getProductAttributesCount(Serializable productId) {
    DetachedCriteria dc = buildProductAttributeCriteria();
    dc.add(Restrictions.eq("product.id", productId));
    dc.setProjection(Projections.rowCount());
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    Criteria criteria = dc.getExecutableCriteria(session);
    Number totalRecords = (Number) criteria.uniqueResult();
    return totalRecords.longValue();
  }

  @SuppressWarnings("unchecked")
  @Override
  public List<FlatProductItemVO> getProductItems(int firstResult, int maxResults, List<Order> orders) {
    DetachedCriteria dc = buildProductAttributeCriteria();
    dc.createCriteria("product", "p", JoinType.INNER_JOIN);
    dc.createCriteria("productItemPrices", "price", JoinType.LEFT_OUTER_JOIN);
    ProjectionList projectionList = Projections.projectionList();
    projectionList.add(Projections.id(), "id");
    projectionList.add(Projections.property("name"), "name");
    projectionList.add(Projections.property("stockKeepingUnit"), "sku");
    projectionList.add(Projections.property("version"), "version");
    projectionList.add(Projections.property("p.name"), "productName");
    projectionList.add(Projections.count("price.id"), "itemPriceCount");
    projectionList.add(Projections.groupProperty("id"));
    dc.setProjection(projectionList);
    if (orders != null && !orders.isEmpty()) {
      for (Order order : orders) {
        dc.addOrder(order);
      }
    }
    dc.setResultTransformer(new AliasToBeanResultTransformer(FlatProductItemVO.class));
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    Criteria criteria = dc.getExecutableCriteria(session);
    if (firstResult > 0) {
      criteria.setFirstResult(firstResult);
    }
    if (maxResults > 0) {
      criteria.setMaxResults(maxResults);
    }
    List<FlatProductItemVO> list = criteria.list();
    return list;
  }

  @Override
  public Long getProductItemsCount() {
    DetachedCriteria dc = buildProductAttributeCriteria();
    dc.setProjection(Projections.rowCount());
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    Criteria criteria = dc.getExecutableCriteria(session);
    Number totalRecords = (Number) criteria.uniqueResult();
    return totalRecords.longValue();
  }

  @SuppressWarnings("unchecked")
  @Override
  public List<FlatProductItemSearchVO> getProductItemSearch(int firstResult, int maxResults,
      List<Order> orders) {
    DetachedCriteria dc = buildProductAttributeCriteria();
    dc.createCriteria("product", "p", JoinType.INNER_JOIN);
    ProjectionList projectionList = Projections.projectionList();
    projectionList.add(Projections.id(), "id");
    projectionList.add(Projections.property("name"), "name");
    projectionList.add(Projections.property("stockKeepingUnit"), "sku");
    projectionList.add(Projections.property("version"), "version");
    projectionList.add(Projections.property("p.name"), "productName");
    dc.setProjection(projectionList);
    if (orders != null && !orders.isEmpty()) {
      for (Order order : orders) {
        dc.addOrder(order);
      }
    }
    dc.setResultTransformer(new AliasToBeanResultTransformer(FlatProductItemSearchVO.class));
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    Criteria criteria = dc.getExecutableCriteria(session);
    if (firstResult > 0) {
      criteria.setFirstResult(firstResult);
    }
    if (maxResults > 0) {
      criteria.setMaxResults(maxResults);
    }
    List<FlatProductItemSearchVO> list = criteria.list();
    if (list != null && !list.isEmpty()) {
      for (FlatProductItemSearchVO vo : list) {
        String desc = vo.getProductName() + " " + Nvl.nvl(vo.getName());
        vo.setDescription(desc.trim());
      }
    }
    return list;
  }

  @Override
  public Long getProductItemSearchCount() {
    DetachedCriteria dc = buildProductAttributeCriteria();
    dc.setProjection(Projections.rowCount());
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    Criteria criteria = dc.getExecutableCriteria(session);
    Number totalRecords = (Number) criteria.uniqueResult();
    return totalRecords.longValue();
  }

  @Override
  public ProductAttribute loadProductAttribute(Serializable id) {
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    ProductAttribute productAttribute = (ProductAttribute) session.load(ProductAttribute.class, id);
    return productAttribute;
  }

  @Override
  public void saveProductAttribute(ProductAttribute productAttribute) {
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    session.save(productAttribute);
  }

  @Override
  public void updateProductAttribute(ProductAttribute productAttribute) {
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    session.update(productAttribute);
  }
}
